/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.meta.service.dbswitch.service.impl.metadata;

import com.google.common.base.Strings;
import com.je.ibatis.extension.plugins.pagination.Page;
import com.je.meta.model.database.SchemaTableData;
import com.je.meta.model.database.SchemaTableMeta;
import com.je.meta.model.database.TableDescription;
import com.je.meta.service.dbswitch.eneity.DatabaseConnectionEntity;
import com.je.meta.service.dbswitch.model.response.*;
import com.je.meta.service.dbswitch.service.DbConnectionService;
import com.je.meta.service.dbswitch.service.IMetaDataByJdbcService;
import com.je.meta.service.dbswitch.service.MetaDataService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class MetaDataServiceImpl implements MetaDataService {

    @Autowired
    private DbConnectionService connectionService;

    public List<MetadataSchemaDetailResponse> allSchemas() {
        DatabaseConnectionEntity dbConn = connectionService.getDatabaseConnection();
        IMetaDataByJdbcService metaDataService = connectionService.getMetaDataCoreService(dbConn);
        List<String> schemas = metaDataService.querySchemaList(
                dbConn.getUrl(), dbConn.getUsername(), dbConn.getPassword());
        List<MetadataSchemaDetailResponse> responses = schemas.stream()
                .map(s -> new MetadataSchemaDetailResponse(dbConn.getName(), s))
                .collect(Collectors.toList());
        return responses;
    }

    @Override
    public Page allTables(String schema, String productId, String fuzzyValue, int intPage, int intLimit) {
        schema = getSchema(schema);
        DatabaseConnectionEntity dbConn = connectionService.getDatabaseConnection();
        IMetaDataByJdbcService metaDataService = connectionService.getMetaDataCoreService(dbConn);
        Page<TableDescription> resultPage = metaDataService.queryTableList(
                dbConn.getUrl(), dbConn.getUsername(), dbConn.getPassword(), schema, productId, fuzzyValue, intPage, intLimit);
        List<MetadataTableInfoResponse> responses = resultPage.getRecords().stream()
                .map(one -> MetadataTableInfoResponse.builder()
                        .tableName(one.getTableCode())
                        .schemaName(one.getSchemaName())
                        .remarks(one.getTableName())
                        .type(one.getTableType())
                        .metaIsCreate(one.getMetaIsCreate())
                        .keyGeneratorName(one.getKeyGeneratorName())
                        .keyGeneratorType(one.getKeyGeneratorType())
                ).collect(Collectors.toList());
        Page page = new Page(intPage, intLimit);
        page.setRecords(responses);
        page.setTotal(resultPage.getTotal());
        return page;
    }

    public MetadataTableDetailResponse tableDetail(String schema, String table) {
        schema = getSchema(schema);
        DatabaseConnectionEntity dbConn = connectionService.getDatabaseConnection();
        IMetaDataByJdbcService metaDataService = connectionService.getMetaDataCoreService(dbConn);
        SchemaTableMeta tableMeta = metaDataService.queryTableMeta(
                dbConn.getUrl(),
                dbConn.getUsername(),
                dbConn.getPassword(),
                schema,
                table);
        return buildMetadataTableDetailResponse(tableMeta);
    }

    public List<MetadataTableDetailResponse> tablesDetailVo(String schema, String tables) {
        List<MetadataTableDetailResponse> list = new ArrayList<>();
        schema = getSchema(schema);
        DatabaseConnectionEntity dbConn = connectionService.getDatabaseConnection();
        IMetaDataByJdbcService metaDataService = connectionService.getMetaDataCoreService(dbConn);
        List<SchemaTableMeta> tableMetas = metaDataService.queryTablesMeta(
                dbConn.getUrl(),
                dbConn.getUsername(),
                dbConn.getPassword(),
                schema,
                tables);
        for (SchemaTableMeta tableMeta : tableMetas) {
            list.add(buildMetadataTableDetailResponse(tableMeta));
        }
        return list;
    }

    @Override
    public void updateTableColumnMeta(String schema, String tables, String syParent, String productId) {
        schema = getSchema(schema);
        DatabaseConnectionEntity dbConn = connectionService.getDatabaseConnection();
        IMetaDataByJdbcService metaDataService = connectionService.getMetaDataCoreService(dbConn);
        List<SchemaTableMeta> tableMetas = metaDataService.queryTablesMeta(
                dbConn.getUrl(),
                dbConn.getUsername(),
                dbConn.getPassword(),
                schema,
                tables);
        metaDataService.updateTableColumnMeta(tableMetas, syParent, productId);
    }

    public SchemaTableDataResponse tableData(String schema, String table) {
        schema = getSchema(schema);
        DatabaseConnectionEntity dbConn = connectionService.getDatabaseConnection();
        IMetaDataByJdbcService metaDataService = connectionService.getMetaDataCoreService(dbConn);
        SchemaTableData data = metaDataService.queryTableData(
                dbConn.getUrl(), dbConn.getUsername(), dbConn.getPassword(),
                schema, table, 10);
        return SchemaTableDataResponse.builder()
                .schemaName(data.getSchemaName())
                .tableName(data.getTableName())
                .columns(data.getColumns())
                .rows(convertRows(data.getColumns(), data.getRows()));
    }


    public String importTable(String schema, String tables, String syParent, String productId) {
        schema = getSchema(schema);
        updateTableColumnMeta(schema, tables, syParent, productId);
        return "导入成功！";
    }

    private String getSchema(String schema) {
        if (Strings.isNullOrEmpty(schema)) {
            schema = allSchemas().get(0).getSchema();
        }
        return schema;
    }

    private MetadataTableDetailResponse buildMetadataTableDetailResponse(SchemaTableMeta tableMeta) {
        List<String> pks = tableMeta.getPrimaryKeys();
        List<MetadataColumnDetailResponse> columnDetailResponses = tableMeta.getColumns().stream()
                .map(one -> MetadataColumnDetailResponse.builder()
                        .fieldName(one.getFieldName())
                        .typeName(one.getFieldTypeName())
                        .typeClassName(one.getFiledTypeClassName())
                        .fieldType(String.valueOf(one.getFieldType()))
                        .displaySize(String.valueOf(one.getDisplaySize()))
                        .precisionSize(String.valueOf(one.getPrecisionSize()))
                        .scaleSize(String.valueOf(one.getScaleSize()))
                        .isPrimaryKey(
                                toStr(
                                        CollectionUtils.isNotEmpty(pks)
                                                && pks.contains(one.getFieldName())))
                        .isAutoIncrement(toStr(one.isAutoIncrement()))
                        .isNullable(toStr(one.isNullable()))
                        .remarks(one.getRemarks())
                ).collect(Collectors.toList());
        return MetadataTableDetailResponse.builder()
                .tableName(tableMeta.getTableCode())
                .schemaName(tableMeta.getSchemaName())
                .remarks(tableMeta.getTableName())
                .type(tableMeta.getTableType())
                .createSql(tableMeta.getCreateSql())
                .primaryKeys(tableMeta.getPrimaryKeys())
                .columns(columnDetailResponses);
    }

    private List<Map<String, Object>> convertRows(List<String> columns, List<List<Object>> rows) {
        if (null == rows || rows.isEmpty()) {
            return Collections.emptyList();
        }
        List<Map<String, Object>> result = new ArrayList<>(rows.size());
        for (List<Object> row : rows) {
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < row.size(); ++i) {
                map.put(columns.get(i), row.get(i));
            }
            result.add(map);
        }
        return result;
    }

    private String toStr(Boolean value) {
        if (null == value) {
            return "未知";
        }
        if (value) {
            return "是";
        }
        return "否";
    }

}
